package cn.lzx.transaction.annotation;

import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Created by lzx on 2016/12/9.
 * <p>
 * spring事务回滚机制(此包下的类验证动态代理的CGLIB代理方式)
 * <p>
 * 1.默认不配置，事务回滚unchecked exception和error，换句话说也就是只回滚RuntimeException和Error的子类和自身
 * 也就是说想回滚必须继承RuntimeException或Error
 * <p>
 * 2.如果配置了回滚异常类型，那么事务回滚该类型以及子类
 */
@Service
public class BusinessService {

    @Autowired
    private CustomerService customerService;

    @Autowired
    private ProducerService producerService;


    /**
     * 测试外部类直接调用标注了@Transactional的方法
     * <p>
     * 默认为unchecked Exception or error 及子类回滚,否则需要定义回滚异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void directlyExchange(Long producerId, Long customerId, Long price) throws Exception {
        customerService.buy(customerId, price);
        producerService.outBuy(producerId, price);
    }

//***********************************************************************************************************************

    /**
     * 测试外部类间调用标注了@Transactional的方法（此方法必须为public权限。如果为private,protected权限，则事务失效）
     * 此时必须使用代理实例去调用，否则无法触发拦截器作用
     * <p>
     * 默认为unchecked Exception or error 及子类回滚,否则需要定义回滚异常
     */
    public void indirectlyExchange(Long producerId, Long customerId, Long price) throws Exception {
        BusinessService currentProxy = (BusinessService) AopContext.currentProxy();
        currentProxy.doExchange(producerId, customerId, price);
    }

    @Transactional(rollbackFor = Exception.class)
    private void doExchange(Long producerId, Long customerId, Long price) throws Exception {
        customerService.buy(customerId, price);
        producerService.outBuy(producerId, price);
    }

//***********************************************************************************************************************


    /**
     * 测试被调用方法标注了@Transactional的方法，
     * 方法内调用本类中的私有方法，是否会触发事务回滚
     * <p>
     * 结论：
     * 第二个方法不论是私有还是公有，均会触发回滚
     */
    @Transactional(rollbackFor = Exception.class)
    public void exchange(Long producerId, Long customerId, Long price) throws Exception {
        doBuy(customerId, price);
        producerService.outBuy(producerId, price);
    }


    private void doBuy(Long customerId, Long price) {
        customerService.buy(customerId, price);
    }


//***********************************************************************************************************************


    /**
     * 测试私有方法调用被标注了@Transactional的方法
     * 被标注@Transactional的方法为public或private或default
     * <p>
     * 结论：
     * 标注了@Transactional方法
     * private：报错，因为代理类无法访问到该方法，报NPE.
     * public：正常回滚.
     * default：不能正常回滚.
     */
    @Transactional(rollbackFor = Exception.class)
    public void doAgent(Long producerId, Long customerId, Long price) throws Exception {
        customerService.buy(customerId, price);
        producerService.outBuy(producerId, price);
    }


    private void agent(Long producerId, Long customerId, Long price) throws Exception {
        BusinessService proxy = (BusinessService) AopContext.currentProxy();
        proxy.doAgent(producerId, customerId, price);
    }

    // 暴露给调用者
    public void invokeByAgent(Long producerId, Long customerId, Long price) throws Exception {
        agent(producerId, customerId, price);
    }


}
